"use strict";
var e, t = (e = require("crypto")) && "object" == typeof e && "default" in e ? e.default : e;
const n = {
	TOKEN_EXPIRED: "uni-id-token-expired",
	CHECK_TOKEN_FAILED: "uni-id-check-token-failed",
	PARAM_REQUIRED: "uni-id-param-required",
	ACCOUNT_EXISTS: "uni-id-account-exists",
	ACCOUNT_NOT_EXISTS: "uni-id-account-not-exists",
	ACCOUNT_CONFLICT: "uni-id-account-conflict",
	ACCOUNT_BANNED: "uni-id-account-banned",
	ACCOUNT_AUDITING: "uni-id-account-auditing",
	ACCOUNT_AUDIT_FAILED: "uni-id-account-audit-failed",
	ACCOUNT_CLOSED: "uni-id-account-closed"
};

function i(e) {
	return !!e && ("object" == typeof e || "function" == typeof e) && "function" == typeof e.then
}

function r(e) {
	if (!e) return;
	const t = e.match(/^(\d+).(\d+).(\d+)/);
	return t ? t.slice(1, 4).map(e => parseInt(e)) : void 0
}

function o(e, t) {
	const n = r(e),
		i = r(t);
	return n ? i ? function(e, t) {
		const n = Math.max(e.length, t.length);
		for (let i = 0; i < n; i++) {
			const n = e[i],
				r = t[i];
			if (n > r) return 1;
			if (n < r) return -1
		}
		return 0
	}(n, i) : 1 : i ? -1 : 0
}
const s = {
	"uni-id-token-expired": 30203,
	"uni-id-check-token-failed": 30202
};

function c(e) {
	const {
		errCode: t,
		errMsgValue: n
	} = e;
	e.errMsg = this._t(t, n), t in s && (e.code = s[t]), delete e.errMsgValue
}

function a(e) {
	return "object" === (i = e, Object.prototype.toString.call(i).slice(8, -1).toLowerCase()) && e.errCode && (t = e
		.errCode, Object.values(n).includes(t)) && !!e.errCode;
	var t, i
}
let u = {
	"zh-Hans": {
		"uni-id-token-expired": "登录状态失效，token已过期",
		"uni-id-check-token-failed": "token校验未通过",
		"uni-id-param-required": "缺少参数: {param}",
		"uni-id-account-exists": "此账号已注册",
		"uni-id-account-not-exists": "此账号未注册",
		"uni-id-account-conflict": "用户账号冲突",
		"uni-id-account-banned": "从账号已封禁",
		"uni-id-account-auditing": "此账号正在审核中",
		"uni-id-account-audit-failed": "此账号审核失败",
		"uni-id-account-closed": "此账号已注销"
	},
	en: {
		"uni-id-token-expired": "The login status is invalid, token has expired",
		"uni-id-check-token-failed": "Check token failed",
		"uni-id-param-required": "Parameter required: {param}",
		"uni-id-account-exists": "Account exists",
		"uni-id-account-not-exists": "Account does not exists",
		"uni-id-account-conflict": "User account conflict",
		"uni-id-account-banned": "Account has been banned",
		"uni-id-account-auditing": "Account audit in progress",
		"uni-id-account-audit-failed": "Account audit failed",
		"uni-id-account-closed": "Account has been closed"
	}
};
try {
	const e = require.resolve("uni-config-center/uni-id/lang/index.js");
	u = function(e, t) {
		const n = Object.keys(e);
		n.push(...Object.keys(t));
		const i = {};
		for (let r = 0; r < n.length; r++) {
			const o = n[r];
			i[o] = Object.assign({}, e[o], t[o])
		}
		return i
	}(u, require(e))
} catch (e) {}
var d = u;

function l(e) {
	return e.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_")
}

function h(e) {
	return JSON.parse((t = function(e) {
		var t = 4 - (e = e.toString()).length % 4;
		if (4 !== t)
			for (var n = 0; n < t; ++n) e += "=";
		return e.replace(/-/g, "+").replace(/_/g, "/")
	}(e), Buffer.from(t, "base64").toString("utf-8")));
	var t
}

function f(e) {
	return l((t = JSON.stringify(e), Buffer.from(t, "utf-8").toString("base64")));
	var t
}

function p(e, n) {
	return l(t.createHmac("sha256", n).update(e).digest("base64"))
}
const k = function(e, t) {
		if ("string" != typeof e) throw new Error("Invalid token");
		const n = e.split(".");
		if (3 !== n.length) throw new Error("Invalid token");
		const [i, r, o] = n;
		if (p(i + "." + r, t) !== o) throw new Error("Invalid token");
		const s = h(i);
		if ("HS256" !== s.alg || "JWT" !== s.typ) throw new Error("Invalid token");
		const c = h(r);
		if (1e3 * c.exp < Date.now()) {
			const e = new Error("Token expired");
			throw e.name = "TokenExpiredError", e
		}
		return c
	},
	g = function(e, t, n = {}) {
		const {
			expiresIn: i
		} = n;
		if (!i) throw new Error("expiresIn is required");
		const r = parseInt(Date.now() / 1e3),
			o = {
				...e,
				iat: r,
				exp: r + n.expiresIn
			},
			s = f({
				alg: "HS256",
				typ: "JWT"
			}) + "." + f(o);
		return s + "." + p(s, t)
	},
	_ = uniCloud.database(),
	I = _.command,
	C = _.collection("uni-id-users"),
	T = _.collection("uni-id-roles");
class E {
	constructor({
		uniId: e
	} = {}) {
		this.uid = null, this.userRecord = null, this.userPermission = null, this.oldToken = null, this
			.oldTokenPayload = null, this.uniId = e, this.config = this.uniId._getConfig(), this.clientInfo = this
			.uniId._clientInfo, this.checkConfig()
	}
	checkConfig() {
		const {
			tokenExpiresIn: e,
			tokenExpiresThreshold: t
		} = this.config;
		if (t >= e) throw new Error("Config error, tokenExpiresThreshold should be less than tokenExpiresIn");
		t > e / 2 && console.warn(
			`Please check whether the tokenExpiresThreshold configuration is set too large, tokenExpiresThreshold: ${t}, tokenExpiresIn: ${e}`
			)
	}
	get customToken() {
		return this.uniId.interceptorMap.get("customToken")
	}
	isTokenInDb(e) {
		return o(e, "1.0.10") >= 0
	}
	async getUserRecord() {
		if (this.userRecord) return this.userRecord;
		const e = await C.doc(this.uid).get();
		if (this.userRecord = e.data[0], !this.userRecord) throw {
			errCode: n.ACCOUNT_NOT_EXISTS
		};
		switch (this.userRecord.status) {
			case void 0:
			case 0:
				break;
			case 1:
				throw {
					errCode: n.ACCOUNT_BANNED
				};
			case 2:
				throw {
					errCode: n.ACCOUNT_AUDITING
				};
			case 3:
				throw {
					errCode: n.ACCOUNT_AUDIT_FAILED
				};
			case 4:
				throw {
					errCode: n.ACCOUNT_CLOSED
				}
		}
		if (this.oldTokenPayload) {
			if (this.isTokenInDb(this.oldTokenPayload.uniIdVersion)) {
				if (-1 === (this.userRecord.token || []).indexOf(this.oldToken)) throw {
					errCode: n.CHECK_TOKEN_FAILED
				}
			}
			if (this.userRecord.valid_token_date && this.userRecord.valid_token_date > 1e3 * this.oldTokenPayload
				.iat) throw {
				errCode: n.TOKEN_EXPIRED
			}
		}
		return this.userRecord
	}
	async updateUserRecord(e) {
		await C.doc(this.uid).update(e)
	}
	async getUserPermission() {
		if (this.userPermission) return this.userPermission;
		const e = (await this.getUserRecord()).role || [];
		if (0 === e.length) return this.userPermission = {
			role: [],
			permission: []
		}, this.userPermission;
		if (e.includes("admin")) return this.userPermission = {
			role: e,
			permission: []
		}, this.userPermission;
		const t = await T.where({
				role_id: I.in(e)
			}).get(),
			n = (i = t.data.reduce((e, t) => (t.permission && e.push(...t.permission), e), []), Array.from(new Set(
				i)));
		var i;
		return this.userPermission = {
			role: e,
			permission: n
		}, this.userPermission
	}
	async _createToken({
		uid: e,
		role: t,
		permission: i
	} = {}) {
		if (!t || !i) {
			const e = await this.getUserPermission();
			t = e.role, i = e.permission
		}
		let r = {
			uid: e,
			role: t,
			permission: i
		};
		if (this.uniId.interceptorMap.has("customToken")) {
			const n = this.uniId.interceptorMap.get("customToken");
			if ("function" != typeof n) throw new Error("Invalid custom token file");
			r = await n({
				uid: e,
				role: t,
				permission: i
			})
		}
		const o = Date.now(),
			{
				tokenSecret: s,
				tokenExpiresIn: c,
				maxTokenLength: a = 10
			} = this.config,
			u = g({
				...r,
				uniIdVersion: "1.0.18"
			}, s, {
				expiresIn: c
			}),
			d = await this.getUserRecord(),
			l = (d.token || []).filter(e => {
				try {
					const t = this._checkToken(e);
					if (d.valid_token_date && d.valid_token_date > 1e3 * t.iat) return !1
				} catch (e) {
					if (e.errCode === n.TOKEN_EXPIRED) return !1
				}
				return !0
			});
		return l.push(u), l.length > a && l.splice(0, l.length - a), await this.updateUserRecord({
			last_login_ip: this.clientInfo.clientIP,
			last_login_date: o,
			token: l
		}), {
			token: u,
			tokenExpired: o + 1e3 * c
		}
	}
	async createToken({
		uid: e,
		role: t,
		permission: i
	} = {}) {
		if (!e) throw {
			errCode: n.PARAM_REQUIRED,
			errMsgValue: {
				param: "uid"
			}
		};
		this.uid = e;
		const {
			token: r,
			tokenExpired: o
		} = await this._createToken({
			uid: e,
			role: t,
			permission: i
		});
		return {
			errCode: 0,
			token: r,
			tokenExpired: o
		}
	}
	async refreshToken({
		token: e
	} = {}) {
		if (!e) throw {
			errCode: n.PARAM_REQUIRED,
			errMsgValue: {
				param: "token"
			}
		};
		this.oldToken = e;
		const t = this._checkToken(e);
		this.uid = t.uid, this.oldTokenPayload = t;
		const {
			uid: i
		} = t, {
			role: r,
			permission: o
		} = await this.getUserPermission(), {
			token: s,
			tokenExpired: c
		} = await this._createToken({
			uid: i,
			role: r,
			permission: o
		});
		return {
			errCode: 0,
			token: s,
			tokenExpired: c
		}
	}
	_checkToken(e) {
		const {
			tokenSecret: t
		} = this.config;
		let i;
		try {
			i = k(e, t)
		} catch (e) {
			if ("TokenExpiredError" === e.name) throw {
				errCode: n.TOKEN_EXPIRED
			};
			throw {
				errCode: n.CHECK_TOKEN_FAILED
			}
		}
		return i
	}
	async checkToken(e, {
		autoRefresh: t = !0
	} = {}) {
		if (!e) throw {
			errCode: n.CHECK_TOKEN_FAILED
		};
		this.oldToken = e;
		const i = this._checkToken(e);
		this.uid = i.uid, this.oldTokenPayload = i;
		const {
			tokenExpiresThreshold: r
		} = this.config, {
			uid: o,
			role: s,
			permission: c
		} = i, a = {
			role: s,
			permission: c
		};
		if (!s && !c) {
			const {
				role: e,
				permission: t
			} = await this.getUserPermission();
			a.role = e, a.permission = t
		}
		if (!r || !t) {
			const e = {
				code: 0,
				errCode: 0,
				...i,
				...a
			};
			return delete e.uniIdVersion, e
		}
		const u = Date.now();
		let d = {};
		1e3 * i.exp - u < 1e3 * r && (d = await this._createToken({
			uid: o
		}));
		const l = {
			code: 0,
			errCode: 0,
			...i,
			...a,
			...d
		};
		return delete l.uniIdVersion, l
	}
}
var m = Object.freeze({
	__proto__: null,
	checkToken: async function(e, {
		autoRefresh: t = !0
	} = {}) {
		return new E({
			uniId: this
		}).checkToken(e, {
			autoRefresh: t
		})
	},
	createToken: async function({
		uid: e,
		role: t,
		permission: n
	} = {}) {
		return new E({
			uniId: this
		}).createToken({
			uid: e,
			role: t,
			permission: n
		})
	},
	refreshToken: async function({
		token: e
	} = {}) {
		return new E({
			uniId: this
		}).refreshToken({
			token: e
		})
	}
});
const w = require("uni-config-center")({
	pluginId: "uni-id"
});
class x {
	constructor({
		context: e,
		clientInfo: t,
		config: n
	} = {}) {
		this._clientInfo = e ? function(e) {
				return {
					appId: e.APPID,
					platform: e.PLATFORM,
					locale: e.LOCALE,
					clientIP: e.CLIENTIP,
					deviceId: e.DEVICEID
				}
			}(e) : t, this._config = n, this.config = this._getOriginConfig(), this.interceptorMap = new Map, w
			.hasFile("custom-token.js") && this.setInterceptor("customToken", require(w.resolve(
			"custom-token.js")));
		this._i18n = uniCloud.initI18n({
			locale: this._clientInfo.locale,
			fallbackLocale: "zh-Hans",
			messages: JSON.parse(JSON.stringify(d))
		}), d[this._i18n.locale] || this._i18n.setLocale("zh-Hans")
	}
	setInterceptor(e, t) {
		this.interceptorMap.set(e, t)
	}
	_t(...e) {
		return this._i18n.t(...e)
	}
	_parseOriginConfig(e) {
		return Array.isArray(e) ? e : e[0] ? Object.values(e) : e
	}
	_getOriginConfig() {
		if (this._config) return this._config;
		if (w.hasFile("config.json")) {
			let e;
			try {
				e = w.config()
			} catch (e) {
				throw new Error("Invalid uni-id config file\n" + e.message)
			}
			return this._parseOriginConfig(e)
		}
		try {
			return this._parseOriginConfig(require("uni-id/config.json"))
		} catch (e) {
			throw new Error("Invalid uni-id config file")
		}
	}
	_getAppConfig() {
		const e = this._getOriginConfig();
		return Array.isArray(e) ? e.find(e => e.dcloudAppid === this._clientInfo.appId) || e.find(e => e
			.isDefaultConfig) : e
	}
	_getPlatformConfig() {
		const e = this._getAppConfig();
		if (!e) throw new Error(
			`Config for current app (${this._clientInfo.appId}) was not found, please check your config file or client appId`
			);
		let t;
		switch (["app-plus", "app-android", "app-ios"].indexOf(this._clientInfo.platform) > -1 && (this._clientInfo
				.platform = "app"), "h5" === this._clientInfo.platform && (this._clientInfo.platform = "web"), this
			._clientInfo.platform) {
			case "web":
				t = "h5";
				break;
			case "app":
				t = "app-plus"
		}
		const n = [{
			tokenExpiresIn: 7200,
			tokenExpiresThreshold: 1200,
			passwordErrorLimit: 6,
			passwordErrorRetryTime: 3600
		}, e];
		t && e[t] && n.push(e[t]), n.push(e[this._clientInfo.platform]);
		const i = Object.assign(...n);
		return ["tokenSecret", "tokenExpiresIn"].forEach(e => {
			if (!i || !i[e]) throw new Error(`Config parameter missing, ${e} is required`)
		}), i
	}
	_getConfig() {
		return this._getPlatformConfig()
	}
}
for (const e in m) x.prototype[e] = m[e];

function y(e) {
	const t = new x(e);
	return new Proxy(t, {
		get(e, t) {
			if (t in e && 0 !== t.indexOf("_")) {
				if ("function" == typeof e[t]) return (n = e[t], function() {
					let e;
					try {
						e = n.apply(this, arguments)
					} catch (e) {
						if (a(e)) return c.call(this, e), e;
						throw e
					}
					return i(e) ? e.then(e => (a(e) && c.call(this, e), e), e => {
						if (a(e)) return c.call(this, e), e;
						throw e
					}) : (a(e) && c.call(this, e), e)
				}).bind(e);
				if ("context" !== t && "config" !== t) return e[t]
			}
			var n
		}
	})
}
x.prototype.createInstance = y;
const O = {
	createInstance: y
};
module.exports = O;